#include "napi/native_api.h"
#include "multimedia/image_framework/image_packer_mdk.h"
#include <bits/alltypes.h>
#include <malloc.h>
#include <string.h>

#define PACKING_QUALITY 100
#define PACKING_BUFFER_SIZE 262144000 // 25 * 1024 * 1024
static napi_value createResult(napi_env env, int32_t res, uint8_t *buffer, size_t actSize) {
    napi_value result = nullptr;
    napi_create_object(env, &result);

    napi_value nRes = nullptr;
    napi_create_int32(env, res, &nRes);
    napi_set_named_property(env, result, "res", nRes);

    napi_value nBuffer = nullptr;
    napi_get_undefined(env, &nBuffer);
    if (buffer != nullptr && actSize != 0) {
        uint8_t *nBufferPtr = nullptr;
        napi_create_arraybuffer(env, actSize, (void **)&nBufferPtr, &nBuffer);
        if (nBufferPtr != nullptr) {
            memcpy(nBufferPtr, buffer, actSize);
        }
    }
    napi_set_named_property(env, result, "buffer", nBuffer);
    return result;
}
// packToBuffer(source:ImageSource): {res:number, buffer:ArrayBuffer}
static napi_value PackToBuffer(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_value args[1] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_value imagePacker = nullptr;
    int32_t res = OH_ImagePacker_Create(env, &imagePacker);
    if (res != IMAGE_RESULT_SUCCESS) {
        return createResult(env, res, nullptr, 0);
    }

    ImagePacker_Native *native = OH_ImagePacker_InitNative(env, imagePacker);
    if (native == nullptr) {
        return createResult(env, IMAGE_RESULT_INVALID_PARAMETER, nullptr, 0);
    }
	NativePixelMap *nativePixelMap = OH_PixelMap_InitNativePixelMap(env, imagePacker);

    size_t bufferSize = PACKING_BUFFER_SIZE;
    uint8_t *buffer = (uint8_t*) malloc(bufferSize);

    ImagePacker_Opts packerOpts;
    packerOpts.format = "image/jpeg";
    packerOpts.quality = PACKING_QUALITY;
    res = OH_ImagePacker_PackToFile(native, nativePixelMap, args[0],&packerOpts);
    OH_ImagePacker_Release(native);
    napi_value result = createResult(env, res, buffer, bufferSize);

    if (buffer != nullptr) {
        free(buffer);
    }
    return result;
}
// packToFile(source:ImageSource, fd:number): number
static napi_value PackToFile(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    napi_value result = nullptr;

    int32_t fd;
    napi_get_value_int32(env, args[1], &fd);
    napi_value imagePacker = nullptr;
    int32_t res = OH_ImagePacker_Create(env, &imagePacker);
    if (res != IMAGE_RESULT_SUCCESS) {
        napi_create_int32(env, res, &result);
        return result;
    }

	ImageSource* imageSource = nullptr;
	res = OH_Image_JsToC_ImageSource(env, args[0], imageSource);
	if (res != IMAGE_RESULT_SUCCESS) {
		return nullptr;
	}
	NativePixelMap *nativePixelMap = OH_PixelMap_InitNativePixelMap(env, imagePacker);
    OhosImagePackerOpts packerOpts;
    packerOpts.format = "image/jpeg";
    packerOpts.quality = PACKING_QUALITY;
    res =  OH_ImagePacker_Packing(imageSource, &packerOpts);
    OH_ImagePacker_Release(native);

    napi_create_int32(env, res, &result);
    return result;
}

static napi_value packerCreate(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    napi_value result = nullptr;

    int32_t fd;
    napi_get_value_int32(env, args[1], &fd);
    //napi_value imagePacker = nullptr;
    ImagePackerCapi* imagePacker  = nullptr;

    int32_t res = OH_ImagePackerCapi_Create(&imagePacker);
    if (res != IMAGE_RESULT_SUCCESS) {
        napi_create_int32(env, res, &result);
        return result;
    }
    napi_value packer = nullptr;
    
    ImagePacker_Native *native = OH_ImagePacker_InitNative(env, packer);
    if (native == nullptr) {
        napi_create_int32(env, IMAGE_RESULT_INVALID_PARAMETER, &result);
        return result;
    }

    OH_ImagePackerCapi_Create(&imagePacker);
    OH_ImagePacker_Release(native);

    napi_create_int32(env, res, &result);
    return result;
}

static napi_value packerRelease(napi_env env, napi_callback_info info) {
    napi_value argValue[SIZE_ONE] = {0};
    size_t argCount = SIZE_ONE;
    ImagePacker_Native* native = getNativeImagePacker(env, info, argValue, argCount);
    if (native == nullptr) {
        DEBUG_LOG("argValue check failed");
        return createResultValue(env, IMAGE_RESULT_INVALID_PARAMETER);
    }
    int32_t res = OH_ImagePacker_Release(native);
    return createResultValue(env, res);
}


static napi_value packerPackingFileSource(napi_env env, napi_callback_info info) {
size_t argc = 2;
    napi_value args[2] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    napi_value result = nullptr;

    int32_t fd;
    napi_get_value_int32(env, args[1], &fd);
    napi_value imagePacker = nullptr;
    int32_t res = OH_ImagePacker_Create(env, &imagePacker);
    if (res != IMAGE_RESULT_SUCCESS) {
        napi_create_int32(env, res, &result);
        return result;
    }

    ImagePackerNative *native = OH_ImagePacker_InitNative(env, imagePacker);
    if (native == nullptr) {
        napi_create_int32(env, IMAGE_RESULT_INVALID_PARAMETER, &result);
        return result;
    }
	ImageSource* imageSource = OH_ImageSource_InitNative(env, imagePacker);
    ImagePacker_Opts packerOpts;
    packerOpts.format = "image/jpeg";
    packerOpts.quality = PACKING_QUALITY;
    res =  OH_ImagePacker_Packing(imageSource, native, &packerOpts);
    OH_ImagePacker_Release(native);

    napi_create_int32(env, res, &result);
    return result;
}

static napi_value packerPackingBufferPixel(napi_env env, napi_callback_info info) {
 size_t argc = 1;
    napi_value args[1] = {nullptr};

    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    napi_value imagePacker = nullptr;
    int32_t res = OH_ImagePacker_Create(env, &imagePacker);
    if (res != IMAGE_RESULT_SUCCESS) {
        return createResult(env, res, nullptr, 0);
    }

    ImagePacker_Native *native = OH_ImagePacker_InitNative(env, imagePacker);
    if (native == nullptr) {
        return createResult(env, IMAGE_RESULT_INVALID_PARAMETER, nullptr, 0);
    }

	ImageSource* imageSource = OH_ImageSource_InitNative(env, imagePacker);
    size_t bufferSize = PACKING_BUFFER_SIZE;
    uint8_t *buffer = (uint8_t*) malloc(bufferSize);

    OhosImagePackerOpts packerOpts;
    packerOpts.format = "image/jpeg";
    packerOpts.quality = PACKING_QUALITY;
    res = OH_ImagePacker_PackToFile(native, imageSource, args[0],&packerOpts);
    OH_ImagePacker_Release(native);
    napi_value result = createResult(env, res, buffer, bufferSize);

    if (buffer != nullptr) {
        free(buffer);
    }
    return result;
}


EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        { "packToBuffer", nullptr, PackToBuffer, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "packToFile", nullptr, PackToFile, nullptr, nullptr, nullptr, napi_default, nullptr },
		{ "packerCreate", nullptr, packerCreate, nullptr, nullptr, nullptr, napi_default, nullptr },
		{ "packerRelease", nullptr, packerRelease, nullptr, nullptr, nullptr, napi_default, nullptr },
		{ "packerPackingFileSource", nullptr, packerPackingFileSource, nullptr, nullptr, nullptr, napi_default, nullptr },
		{ "packerPackingBufferPixel", nullptr, packerPackingBufferPixel, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version =1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "ImagePackingDemo",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterImagePackingDemoModule(void)
{
    napi_module_register(&demoModule);
}
